﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.IO;

namespace Log4View.BusinessModel
{
    public abstract class LogWatcher : ILogWatcher
    {
        public event IncomingMessagesNotify IncomingMessages;

        private readonly string _name;

        private readonly string _watchedFilePath;

        private readonly int _untakenCountThresshold;

        private LogEntityQueue _logEntityQueue;

        private ILogParser _logParser;

        private IFileSeeker _fileSeeker;

        private Thread _watcherThread;

        private volatile bool _keepRunning;

        private volatile bool _isRunning;
        
        public LogWatcher(string name, string watchedFilePath, int historyLength )
        {
            if (string.IsNullOrEmpty(name))
                throw new ArgumentException("name param cann't be null or empty", "name");

            if (!File.Exists(watchedFilePath))
                throw new FileNotFoundException("watchedFilePath should point to existing file", "watchedFilePath");

            if (historyLength < 1 || historyLength > 1000)
                throw new ArgumentException("historyLength param should be in range 1 to 1000", "historyLength");

            _name = name;
            _watchedFilePath = watchedFilePath;
            _logEntityQueue = new LogEntityQueue(historyLength);
            _untakenCountThresshold = 2;
        }

        public IFileSeeker FileSeeker
        {
            get { return _fileSeeker ?? (_fileSeeker = GetFileSeeker()); }
        }

        public ILogParser LogParser
        {
            get { return _logParser ?? (_logParser = GetLogParser()); }
        }

        public LogEntityQueue LogQueue
        {
            get { return _logEntityQueue; }
        }


        public bool IsRunning
        {
            get { return _watcherThread.IsAlive && _isRunning; }
        }

        public void Start(bool waitForStart)
        {
            if (_watcherThread != null)
                Stop();

            PrepareStart();

            _keepRunning = true;
            _watcherThread = new Thread(this.Run);
            _watcherThread.Start();

            while (waitForStart && !IsRunning) _watcherThread.Join(1);
        }

        public void Stop()
        {
            if (_watcherThread == null)
                throw new InvalidOperationException("Cann't stop the run, while it's not running!");
            
            _keepRunning = false;

            _watcherThread.Join(100);
            
            if (_watcherThread.IsAlive)
                _watcherThread.Abort();
        }

        protected virtual void PrepareStart()
        {
            if (FileSeeker == null)
                throw new NotImplementedException("FileSeeker may not return null value");

            if (LogParser == null)
                throw new NotImplementedException("LogParser may not return null value");
        }

        protected virtual void Run()
        {
            _isRunning = true;

            while (_keepRunning)
            {
                LogParser.Parse(FileSeeker.Read());
                CheckLogEntityQueue();
            }
            
            _isRunning = false;
        }

        protected void CheckLogEntityQueue()
        {
            if (_logEntityQueue.Count > _untakenCountThresshold)
            {
                RaiseIncomingMessagesEvent();
            }
        }

        protected abstract ILogParser GetLogParser();

        protected abstract IFileSeeker GetFileSeeker();

        private void RaiseIncomingMessagesEvent()
        {
            var handler = IncomingMessages;

            if (handler != null)
                handler();
        }
    }
}
